var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

import React, { Component, cloneElement, PureComponent } from 'react';
import PropTypes from 'prop-types';

import { rectIsCrossOrContain, rectIsInFrontOnHor, rectIsInFrontOnVer, extendRect } from 'rect-rel';

/* 
# Affecter : 影响者

## 特性：
根据子元素与其与特定1个或者多位置的距离对子元素设置样式;

## props
affectAnchors : [{x:number,y:number}]?    影响锚点在Affecter的视口上的偏移坐标；
usePixelCoordInAffecter ?:boolean 表示affectAnchors中的坐标单位是否是像素，当为 false 或者未定义时，affectAnchors 中的 x 表示 影响锚点在X轴方向上相对于Affecter的宽度的偏移比例，y 表示 影响锚点在Y轴方向上相对于Affecter的高度的偏移比例；
itemAnchor ?:{x:ScaleNumber,y:ScaleNumber} = {x:0.5,y:0.5}      项目的锚点，用于计算与影响锚点之间的距离，默认值是{x:0.5,y:0.5}
transforms ?: [ (distance:{x:number,y:number},index:number,distanceArr:[{x:number,y:number}],,itemRect :{x:number,y:number,width:number,height:number},itemElement?:Element,containerElement?:Element)=>{x:number,y:number} ]    转换函数数组，里面包含的函数是用来转换项目元素的锚点与影响锚点之间的距离坐标，并返回转换后的距离坐标对象；
getItemAffectStyle ?: (distanceArr : [{x:number,y:number}],itemElement?:Element,containerElement?:Element,itemRowCol:{row:number,col:number,index:number},itemRect)=>StyleObject   获得项目影响样式的回调函数；
getItemInitStyle ?: (element,index,computeAffectStyleForItem)=>StyleObject      获得元素的初始样式（比如：用于布局的样式）的回调函数，只在初始化时为每个元素调用并设置一次；

wrapChildren  ?:boolean     表示是否包装子元素；
wrapClass  ?:React的calssName类型      定义包装元素的类
wrapStyle   ?:React的style类型      定义包装元素的样式

loopType ?: "Hor" | "Ver" | "All" | "false"     定义循环类型
wrapSpace ?:number    定义循环包之间的间隔，默认值为0

onScroll ?:function Affecter 的滑动事件处理函数；当返回 true 值时，不会执行 Affecter 的默认操作；
renderExtendRadius  ?:number    项目的渲染范围的扩展半径；





### 性能优化相关的props
throttleDelay ?:number 节流阀体的时间限，即该数值表示多长时间内不允许触发第2次更新
throttleStep  ?:number  节流阀体的步长限，即该数值表示多长的滑动距离才能触发第2次更新


### 计算循环(ComputeLoop)独有的Props
ItemType : ReactComponent   计算循环的项目类型
itemSize :{width,height}    项目的尺寸
horSpace :nunber      项目间的水平间距
verSpace :number      项目间的垂直间距
itemDataArr :Array     项目的数据数组
roundRowCount :number   单个循环周期内的行数
roundColCount :number   单个循环周期内的列数
*/

var Affecter = function (_PureComponent) {
    _inherits(Affecter, _PureComponent);

    function Affecter(props) {
        _classCallCheck(this, Affecter);

        var _this = _possibleConstructorReturn(this, (Affecter.__proto__ || Object.getPrototypeOf(Affecter)).call(this, props));

        _this.state = {
            viewRect: {
                x: 0,
                y: 0,
                width: 0,
                height: 0
            }
        };

        _this.loopWrapArr = [];

        //性能优化
        _this.thenTime = new Date();

        _this.scrollHandle = _this.scrollHandle.bind(_this);
        _this.computeAffectStyleForItem = _this.computeAffectStyleForItem.bind(_this);
        _this.computeDistanceToAffecterFromItem = _this.computeDistanceToAffecterFromItem.bind(_this);
        _this.itemIsInRenderRange = _this.itemIsInRenderRange.bind(_this);
        _this.setThisDom = _this.setThisDom.bind(_this);
        _this.setWrapDom = _this.setWrapDom.bind(_this);
        _this.setLoopWrap = _this.setLoopWrap.bind(_this);

        //计算循环
        _this.computeItemLayoutStyle = _this.computeItemLayoutStyle.bind(_this);
        _this.computeAffectStyleForComputeLoopItem = _this.computeAffectStyleForComputeLoopItem.bind(_this);
        _this.computeItemIndex = _this.computeItemIndex.bind(_this);

        _this.updateForProps(props);
        _this.updateForPropsState(props, _this.state);

        return _this;
    }

    _createClass(Affecter, [{
        key: 'componentDidMount',
        value: function componentDidMount() {
            this.updateViewRect(true);
        }
    }, {
        key: 'componentWillReceiveProps',
        value: function componentWillReceiveProps(nextProps) {
            this.updateForProps(nextProps);
        }
    }, {
        key: 'componentWillUpdate',
        value: function componentWillUpdate(nextProps, nextState) {
            this.updateForPropsState(nextProps, nextState);
        }
    }, {
        key: 'updateForPropsState',
        value: function updateForPropsState(nextProps, nextState) {
            var renderExtendRadius = nextProps.renderExtendRadius,
                ItemType = nextProps.ItemType;


            var viewRect = nextState.viewRect;
            var itemRenderRange = extendRect(viewRect, renderExtendRadius, renderExtendRadius);
            this.itemRenderRange = itemRenderRange;

            if (ItemType) {
                this.coordIndexRange = this.computeCoordIndexRange(itemRenderRange, this.itemAreaSize, nextProps.loopType, viewRect);
            }
        }

        /**
         * 此方法主要用于行初始化工作
         * @param newPops  新的Props
         */

    }, {
        key: 'updateForProps',
        value: function updateForProps(newProps) {
            var getItemInitStyle = newProps.getItemInitStyle,
                children = newProps.children,
                itemAnchor = newProps.itemAnchor,
                wrapChildren = newProps.wrapChildren,
                loopType = newProps.loopType,
                ItemType = newProps.ItemType;


            this.haveWrap = !ItemType && (wrapChildren || loopType);
            itemAnchor.x = itemAnchor.x == undefined ? 0.5 : itemAnchor.x;
            itemAnchor.y = itemAnchor.y == undefined ? 0.5 : itemAnchor.y;
            this.itemAnchor = itemAnchor;

            if (ItemType) {
                this.updateForComputeLoopWithProps(newProps);
            } else if (getItemInitStyle) {

                var computeAffectStyleForItem = this.computeAffectStyleForItem;
                var newChildrens = React.Children.map(children, function (currentElement, index) {
                    var style = currentElement.props.style;
                    var initStyleObj = getItemInitStyle(currentElement, index, computeAffectStyleForItem);
                    var finalStyle = Object.assign({}, style, initStyleObj);
                    return cloneElement(currentElement, { style: finalStyle });
                });
                this.newChildrens = newChildrens;
            } else {
                this.newChildrens = children;
            }
        }

        /**
         * 更新滑动的位置信息
         */

    }, {
        key: 'updateViewRect',
        value: function updateViewRect(isInit) {
            var _props = this.props,
                affectAnchors = _props.affectAnchors,
                loopType = _props.loopType,
                ItemType = _props.ItemType,
                throttleStep = _props.throttleStep;


            if (affectAnchors || loopType) {

                var thisDom = this.thisDom;
                var newX = thisDom.scrollLeft;
                var newY = thisDom.scrollTop;

                var viewRect = this.state.viewRect;
                var stepX = newX - viewRect.x;
                var stepY = newY - viewRect.y;

                if (Math.max(Math.abs(stepX), Math.abs(stepY)) < throttleStep && !isInit) {
                    return;
                }

                if (loopType) {

                    var isBeginX = thisDom.scrollLeft <= 0;
                    var isBeginY = thisDom.scrollTop <= 0;

                    var allowHor = false;
                    var allowVer = false;

                    switch (loopType) {
                        case "All":
                            {
                                allowHor = true;
                                allowVer = true;
                                break;
                            }

                        case "Hor":
                            {
                                allowHor = true;
                                break;
                            }

                        case "Ver":
                            {
                                allowVer = true;
                                break;
                            }
                    }

                    var needScrollX = allowHor && isBeginX && stepX < 0;
                    var needScrollY = allowVer && isBeginY && stepY < 0;

                    if (needScrollX || needScrollY) {

                        var scrollLengthX = 0;
                        var scrollLengthY = 0;

                        if (ItemType) {
                            scrollLengthX = needScrollX ? this.roundSize.width : 0;
                            scrollLengthY = needScrollY ? this.roundSize.height : 0;
                        } else {
                            var wrapData = this.getLoopWrapData();
                            scrollLengthX = needScrollX ? wrapData.width + wrapData.wrapSpace : 0;
                            scrollLengthY = needScrollY ? wrapData.height + wrapData.wrapSpace : 0;
                        }

                        thisDom.scrollBy(scrollLengthX, scrollLengthY);
                    } else {

                        this.setState({
                            viewRect: {
                                x: newX,
                                y: newY,
                                width: thisDom.clientWidth,
                                height: thisDom.clientHeight,
                                stepX: stepX,
                                stepY: stepY
                            }
                        });
                    }
                } else {

                    this.setState({
                        viewRect: {
                            x: newX,
                            y: newY,
                            width: thisDom.clientWidth,
                            height: thisDom.clientHeight
                        }
                    });
                }
            }
        }

        /**
         * 计算元素被影响的样式
         * @param itemElement ?: Dom元素       项目的Dom节点
         * @param orItemRect ?:{x:number,y:number,width:number,height:number}   包含元素的位置和长宽的对象
         * @param itemRowCol :{row:number,col:number,index:number}     包含项目的行号、列号、序号的对象
         * @returns    React的样式对象
         */

    }, {
        key: 'computeAffectStyleForItem',
        value: function computeAffectStyleForItem(itemElement, orItemRect, itemRowCol) {

            var affectStyle = null;

            var itemRect = null;
            var wrapDom = null;
            if (itemElement) {
                wrapDom = itemElement.parentNode;
                itemRect = {
                    x: itemElement.offsetLeft,
                    y: itemElement.offsetTop,
                    width: itemElement.clientWidth,
                    height: itemElement.clientHeight
                };
            } else if (orItemRect) {
                itemRect = orItemRect;
            }

            var _props2 = this.props,
                affectAnchors = _props2.affectAnchors,
                usePixelCoordInAffecter = _props2.usePixelCoordInAffecter,
                getItemAffectStyle = _props2.getItemAffectStyle;


            if (itemRect && affectAnchors && getItemAffectStyle) {

                var initDistanceArr = this.getDistancesToAffecterFromItem(itemRect, affectAnchors, usePixelCoordInAffecter, wrapDom);
                initDistanceArr.publicData = {}; // 所有距离坐标共享的数据
                var transDistanceArr = initDistanceArr;
                var transforms = this.props.transforms;
                var thisDom = this.thisDom;

                if (transforms) {
                    transDistanceArr = transforms.reduce(function (preDistanceArr, currentTransform) {

                        var publicData = preDistanceArr.publicData;
                        var newDistanceArr = preDistanceArr.map(function (distance, index, distanceArr) {
                            return currentTransform(distance, index, distanceArr, itemRect, itemElement, thisDom);
                        }, publicData);
                        newDistanceArr.publicData = publicData;

                        return newDistanceArr;
                    }, initDistanceArr);
                }
                affectStyle = getItemAffectStyle(transDistanceArr, itemElement, this.thisDom, itemRowCol, itemRect);
            }

            return affectStyle;
        }

        /**
         * 获得项目元素与所有影响锚点之间的距离坐标
         * @param itemRect : {x = 0,y = 0,width = 0,height = 0}   包含项目元素的位置和长宽的对象
         * @param affectAnchors : {x = 0,y = 0}   影响坐标
         * @param usePixelCoordInAffecter : boolean   影响坐标的单位是否是像素
         * @param wrapDom ? 包装元素的Dom
         * @returns [{x ?: number, y ?: number}]      返回项目元素与影响锚点之间的距离坐标
         */

    }, {
        key: 'getDistancesToAffecterFromItem',
        value: function getDistancesToAffecterFromItem(itemRect, affectAnchors, usePixelCoordInAffecter, wrapDom) {

            var computeDistanceToAffecterFromItem = this.computeDistanceToAffecterFromItem;
            var viewRect = this.state.viewRect;
            var finalItemRect = itemRect;

            if (this.haveWrap && wrapDom) {
                finalItemRect.x += wrapDom.offsetLeft;
                finalItemRect.y += wrapDom.offsetTop;
            }

            var itemAnchor = this.itemAnchor;
            var distanceArr = affectAnchors.map(function (affectAnchor, index, arr) {
                return computeDistanceToAffecterFromItem(affectAnchor, usePixelCoordInAffecter, viewRect, itemAnchor, finalItemRect);
            });

            return distanceArr;
        }

        /**
         * 提供给item的工具方法，用于判断item是否在可视范围内
         * @param itemElement : Dom   item的Dom
         * @returns {boolean}
         */

    }, {
        key: 'itemIsInRenderRange',
        value: function itemIsInRenderRange(itemElement) {

            var itemX = itemElement.offsetLeft;
            var itemY = itemElement.offsetTop;

            if (this.haveWrap) {
                var wrapDom = itemElement.parentNode;
                itemX += wrapDom.offsetLeft;
                itemY += wrapDom.offsetTop;
            }

            var itemRect = {
                x: itemX,
                y: itemY,
                width: itemElement.clientWidth,
                height: itemElement.clientHeight
            };

            return rectIsCrossOrContain(this.itemRenderRange, itemRect);
        }

        /**
         * 计算项目元素与影响锚点之间的距离坐标
         * @param affectAnchor : {x ?: number, y ?: number}      影响锚点的比例坐标
         * @param usePixelCoordInAffecter ?:boolean 表示affectAnchors中的坐标单位是像素
         * @param viewRect : {x:number,y:number,width:number,height:number}   容器的视口的滑动偏移矩形
         * @param itemAnchor : {x : number, y : number}      项目的锚点
         * @param {x = 0,y = 0,width = 0,height = 0}    项目的位置和长宽
         * @returns {x ?: number, y ?: number}      返回项目元素与影响锚点之间的距离坐标
         */

    }, {
        key: 'computeDistanceToAffecterFromItem',
        value: function computeDistanceToAffecterFromItem(affectAnchor, usePixelCoordInAffecter, viewRect, itemAnchor, _ref) {
            var _ref$x = _ref.x,
                x = _ref$x === undefined ? 0 : _ref$x,
                _ref$y = _ref.y,
                y = _ref$y === undefined ? 0 : _ref$y,
                _ref$width = _ref.width,
                width = _ref$width === undefined ? 0 : _ref$width,
                _ref$height = _ref.height,
                height = _ref$height === undefined ? 0 : _ref$height;


            var distance = { x: 0, y: 0 };

            var _computeAffectAnchorA = this.computeAffectAnchorAbsCoord(affectAnchor, usePixelCoordInAffecter, viewRect),
                _computeAffectAnchorA2 = _computeAffectAnchorA.x,
                affecterX = _computeAffectAnchorA2 === undefined ? 0 : _computeAffectAnchorA2,
                _computeAffectAnchorA3 = _computeAffectAnchorA.y,
                affecterY = _computeAffectAnchorA3 === undefined ? 0 : _computeAffectAnchorA3;

            var anchorCoordX = x + itemAnchor.x * width;
            var anchorCoordY = y + itemAnchor.y * height;

            distance.x = anchorCoordX - affecterX;
            distance.y = anchorCoordY - affecterY;
            return distance;
        }

        /**
         * 计算影响锚点的绝对坐标
         * @param affectAnchor
         * @param usePixelCoordInAffecter
         * @param viewRect
         */

    }, {
        key: 'computeAffectAnchorAbsCoord',
        value: function computeAffectAnchorAbsCoord(affectAnchor, usePixelCoordInAffecter, viewRect) {
            var absCoord = {};

            var affectAnchorX = affectAnchor.x;
            var affectAnchorY = affectAnchor.y;

            var offsetX = viewRect.x,
                offsetY = viewRect.y,
                relLengthX = viewRect.width,
                relLengthY = viewRect.height;


            if (affectAnchorX) {
                var relCoordX = usePixelCoordInAffecter ? affectAnchorX : affectAnchorX * relLengthX;
                absCoord.x = offsetX + relCoordX;
            }

            if (affectAnchorY) {
                var relCoordY = usePixelCoordInAffecter ? affectAnchorY : affectAnchorY * relLengthY;
                absCoord.y = offsetY + relCoordY;
            }

            return absCoord;
        }

        /**
         * 滑动事件处理函数
         * @param event 滑动事件对象
         */

    }, {
        key: 'scrollHandle',
        value: function scrollHandle(event) {

            var onScroll = this.props.onScroll;

            /*
            在以下情况不会触发 updateViewRect()
            - Affecter 组件上有 onScroll 事件处理函数 且 该 onScroll 返回 true 值
            */
            if (!(onScroll && onScroll(event))) {

                //节流
                var now = new Date();
                if (now - this.thenTime >= this.props.throttleDelay) {
                    this.updateViewRect();
                    this.thenTime = now;
                }
            }
        }
    }, {
        key: 'setThisDom',
        value: function setThisDom(element) {
            this.thisDom = element;
        }
    }, {
        key: 'setWrapDom',
        value: function setWrapDom(element) {
            this.wrapDom = element;
        }
    }, {
        key: 'setLoopWrap',
        value: function setLoopWrap(component) {
            this.loopWrapArr.push(component);
        }
    }, {
        key: 'getLoopWrapData',
        value: function getLoopWrapData() {
            var wrapComponent = this.loopWrapArr[0];
            return wrapComponent.wrapData;
        }
    }, {
        key: 'render',
        value: function render() {
            var _props3 = this.props,
                throttleDelay = _props3.throttleDelay,
                throttleStep = _props3.throttleStep,
                ItemType = _props3.ItemType,
                itemSize = _props3.itemSize,
                horSpace = _props3.horSpace,
                verSpace = _props3.verSpace,
                itemDataArr = _props3.itemDataArr,
                roundRowCount = _props3.roundRowCount,
                roundColCount = _props3.roundColCount,
                onScroll = _props3.onScroll,
                style = _props3.style,
                affectAnchors = _props3.affectAnchors,
                usePixelCoordInAffecter = _props3.usePixelCoordInAffecter,
                itemAnchor = _props3.itemAnchor,
                transforms = _props3.transforms,
                getItemInitStyle = _props3.getItemInitStyle,
                getItemAffectStyle = _props3.getItemAffectStyle,
                wrapChildren = _props3.wrapChildren,
                wrapClass = _props3.wrapClass,
                wrapStyle = _props3.wrapStyle,
                loopType = _props3.loopType,
                wrapSpace = _props3.wrapSpace,
                children = _props3.children,
                renderExtendRadius = _props3.renderExtendRadius,
                otherProps = _objectWithoutProperties(_props3, ['throttleDelay', 'throttleStep', 'ItemType', 'itemSize', 'horSpace', 'verSpace', 'itemDataArr', 'roundRowCount', 'roundColCount', 'onScroll', 'style', 'affectAnchors', 'usePixelCoordInAffecter', 'itemAnchor', 'transforms', 'getItemInitStyle', 'getItemAffectStyle', 'wrapChildren', 'wrapClass', 'wrapStyle', 'loopType', 'wrapSpace', 'children', 'renderExtendRadius']);

            var newChildrens = null;
            var finalElement = null;
            var rootStyle = null;

            if (ItemType) {
                //计算循环模式

                newChildrens = [];
                rootStyle = Object.assign({}, style, { position: "relative" });

                if (itemDataArr && itemDataArr.length > 0) {
                    var _coordIndexRange = this.coordIndexRange,
                        rowStart = _coordIndexRange.rowStart,
                        rowEnd = _coordIndexRange.rowEnd,
                        colStart = _coordIndexRange.colStart,
                        colEnd = _coordIndexRange.colEnd;

                    var computeItemLayoutStyle = this.computeItemLayoutStyle;
                    var computeAffectStyleForComputeLoopItem = this.computeAffectStyleForComputeLoopItem;
                    var computeItemIndex = this.computeItemIndex;

                    var newIndexKeyArr = this.creationIndexKeyArr();
                    for (var row = rowStart; row <= rowEnd; row++) {
                        for (var col = colStart; col <= colEnd; col++) {

                            var layoutStyle = computeItemLayoutStyle(row, col);
                            var affectStyle = computeAffectStyleForComputeLoopItem(row, col);
                            var childrenStyle = Object.assign({ position: "absolute" }, layoutStyle, affectStyle);
                            var itemIndex = computeItemIndex(row, col);
                            var itemData = itemDataArr[itemIndex % itemDataArr.length];

                            var itemKey = this.indexKeyArr[itemIndex].pop();
                            if (!itemKey) {
                                var currentDate = new Date();
                                itemKey = '' + row + col + currentDate.getTime();
                            }

                            var childrenProps = {
                                style: childrenStyle,
                                row: row,
                                col: col,
                                itemIndex: itemIndex,
                                itemData: itemData,
                                key: itemKey
                            };

                            newChildrens.push(React.createElement(ItemType, childrenProps));
                            newIndexKeyArr[itemIndex].push(itemKey);
                        }
                    }

                    this.indexKeyArr = newIndexKeyArr;
                }

                finalElement = React.createElement(
                    'div',
                    Object.assign({}, otherProps, { style: rootStyle, onScroll: this.scrollHandle, ref: this.setThisDom }),
                    newChildrens
                );
            } else {

                //克隆节点是为了让子组件也跟着刷新
                var itemIsInRenderRange = this.itemIsInRenderRange;
                var computeAffectStyleForItem = this.computeAffectStyleForItem;
                newChildrens = React.Children.map(this.newChildrens, function (currentElement, index) {
                    return cloneElement(currentElement, { itemIsInRenderRange: itemIsInRenderRange, computeAffectStyleForItem: computeAffectStyleForItem });
                });

                rootStyle = Object.assign({}, style, { position: "relative" }); //如果元素的父元素没有被定位，即没有设置 position，则元素的 offsetLeft 和 offsetTop 即是相对于document元素的；


                if (loopType) {
                    //循环模式

                    var viewRect = this.state.viewRect;
                    var direcX = viewRect.stepX > 0;
                    var direcY = viewRect.stepY > 0;

                    var newWrapStyle = Object.assign({}, wrapStyle, { position: "absolute" });

                    var wrapProps = {
                        className: wrapClass,
                        style: newWrapStyle,
                        viewRect: viewRect,
                        direcX: direcX,
                        direcY: direcY,
                        loopType: loopType,
                        wrapSpace: wrapSpace,
                        ref: this.setLoopWrap
                    };

                    var rootProps = Object.assign({}, otherProps, { onScroll: this.scrollHandle, ref: this.setThisDom });

                    if (loopType === "All") {
                        //任意方向循环模式
                        finalElement = React.createElement(
                            'div',
                            Object.assign({}, rootProps, { style: rootStyle }),
                            React.createElement(
                                LoopWrap,
                                Object.assign({}, wrapProps, { rowCol: '00', key: '00' }),
                                newChildrens
                            ),
                            React.createElement(
                                LoopWrap,
                                Object.assign({}, wrapProps, { rowCol: '01', key: '01' }),
                                newChildrens
                            ),
                            React.createElement(
                                LoopWrap,
                                Object.assign({}, wrapProps, { rowCol: '10', key: '10' }),
                                newChildrens
                            ),
                            React.createElement(
                                LoopWrap,
                                Object.assign({}, wrapProps, { rowCol: '11', key: '11' }),
                                newChildrens
                            )
                        );
                    } else {
                        //单方向循环模式
                        finalElement = React.createElement(
                            'div',
                            Object.assign({}, rootProps, { style: rootStyle }),
                            React.createElement(
                                LoopWrap,
                                Object.assign({}, wrapProps, { rowCol: '00', key: '00' }),
                                newChildrens
                            ),
                            React.createElement(
                                LoopWrap,
                                Object.assign({}, wrapProps, { rowCol: '01', key: '01' }),
                                newChildrens
                            )
                        );
                    }
                } else if (wrapChildren) {
                    //包裹模式
                    finalElement = React.createElement(
                        'div',
                        Object.assign({}, otherProps, { style: rootStyle, onScroll: this.scrollHandle, ref: this.setThisDom }),
                        React.createElement(
                            'div',
                            { className: wrapClass, style: wrapStyle, ref: this.setWrapDom },
                            newChildrens
                        )
                    );
                } else {
                    //正常模式
                    finalElement = React.createElement(
                        'div',
                        Object.assign({}, otherProps, { style: style, onScroll: this.scrollHandle, ref: this.setThisDom }),
                        newChildrens
                    );
                }
            }

            return finalElement;
        }

        //计算循环

        /**
         * 计算布局样式
         * @param row :number  坐标行号
         * @param col :number  坐标列号
         * @returns {position:"absolute",left: string, top: string，width: string,height: string}  项目的布局样式
         */

    }, {
        key: 'computeItemLayoutStyle',
        value: function computeItemLayoutStyle(row, col) {
            var _computeItemCoord = this.computeItemCoord(row, col),
                x = _computeItemCoord.x,
                y = _computeItemCoord.y;

            var _props$itemSize = this.props.itemSize,
                width = _props$itemSize.width,
                height = _props$itemSize.height;

            return {
                position: "absolute",
                left: x + 'px',
                top: y + 'px',
                width: width + 'px',
                height: height + 'px'
            };
        }

        /**
         * 计算项目的矩形
         * @param row :number  坐标行号
         * @param col :number  坐标列号
         * @returns {x: number, y: number,width:number,height:number}   计算项目的矩形
         */

    }, {
        key: 'computeItemRect',
        value: function computeItemRect(row, col) {
            var itemCoord = this.computeItemCoord(row, col);
            var itemRect = Object.assign({}, itemCoord, this.props.itemSize);
            return itemRect;
        }

        /**
         * 计算项目的坐标
         * @param row :number  坐标行号
         * @param col :number  坐标列号
         * @returns {x: number, y: number}    计算项目的坐标
         */

    }, {
        key: 'computeItemCoord',
        value: function computeItemCoord(row, col) {
            var _itemAreaSize = this.itemAreaSize,
                width = _itemAreaSize.width,
                height = _itemAreaSize.height;

            return {
                x: col * width,
                y: row * height
            };
        }

        /**
         * 当props更新时，更新与ComputeLoop（计算循环）相关的配置
         * @param newProps      新的props
         */

    }, {
        key: 'updateForComputeLoopWithProps',
        value: function updateForComputeLoopWithProps(newProps) {
            var itemSize = newProps.itemSize,
                horSpace = newProps.horSpace,
                verSpace = newProps.verSpace,
                itemDataArr = newProps.itemDataArr,
                roundRowCount = newProps.roundRowCount,
                roundColCount = newProps.roundColCount,
                getItemData = newProps.getItemData;


            var itemAreaWidth = itemSize.width + horSpace;
            var itemAreaHeight = itemSize.height + verSpace;

            this.itemAreaSize = {
                width: itemAreaWidth,
                height: itemAreaHeight
            };

            var rowCount = roundRowCount;
            var colCount = roundColCount;
            var roundItemCount = itemDataArr.length;

            if (roundRowCount) {
                colCount = Math.ceil(roundItemCount / roundRowCount);
            } else if (roundColCount) {
                rowCount = Math.ceil(roundItemCount / roundColCount);
            }

            this.roundRowCount = rowCount;
            this.roundColCount = colCount;
            this.roundItemCount = rowCount * colCount;
            this.indexKeyArr = this.creationIndexKeyArr();

            this.roundSize = {
                width: itemAreaWidth * colCount,
                height: itemAreaHeight * rowCount
            };
        }

        /**
         * 根据给定的矩形计算坐标行列号的范围
         * @param renderRect   渲染矩形
         * @param itemAreaSize  项目的尺寸
         * @param loopType   循环类型
         * @param viewRect  视口的矩形
         * 
         * 注意：
         * 行列号是 计算循环为 项目设置的网络坐标，每个网格对应一个项目的位置；
         */

    }, {
        key: 'computeCoordIndexRange',
        value: function computeCoordIndexRange(renderRect, itemAreaSize, loopType, viewRect) {
            var x = renderRect.x,
                y = renderRect.y,
                width = renderRect.width,
                height = renderRect.height;
            var itemAreaWidth = itemAreaSize.width,
                itemAreaHeight = itemAreaSize.height;
            var stepX = viewRect.stepX,
                stepY = viewRect.stepY;


            var direX = stepX >= 0;
            var direY = stepY >= 0;

            var right = x + width;
            var bottom = y + height;

            var computeCoordIndex = this.computeCoordIndex;

            var rowStart = 0;
            var rowEnd = 0;
            var colStart = 0;
            var colEnd = 0;

            /*
            实际上 rowCount 和 colCount 比 实际的 行数少1，但为了提高性能，这里不再加上这个差值，因为后面的计算为把这个差值给弥补过来；
            通过 rowCount 和 colCount 计算 rowEnd 和 colEnd 可以保证，每次渲染的行列数相同
            */
            var startX = renderRect.x < 0 ? 0 : renderRect.x;
            var startY = renderRect.y < 0 ? 0 : renderRect.y;
            switch (loopType) {

                case "Ver":
                    {
                        rowStart = computeCoordIndex(startY, itemAreaHeight);
                        var rowCount = computeCoordIndex(renderRect.height, itemAreaHeight);
                        rowEnd = rowStart + rowCount;
                        var colCount = computeCoordIndex(viewRect.width, itemAreaWidth);
                        colEnd = colStart + colCount;

                        !direY && rowStart > 0 ? rowStart-- : rowEnd++;

                        break;
                    }

                case "Hor":
                    {
                        var _rowCount = computeCoordIndex(viewRect.height, itemAreaHeight);
                        rowEnd = rowStart + _rowCount;
                        colStart = computeCoordIndex(startX, itemAreaWidth);
                        var _colCount = computeCoordIndex(renderRect.width, itemAreaWidth);
                        colEnd = colStart + _colCount;

                        !direX && colStart > 0 ? colStart-- : colEnd++;

                        break;
                    }

                default:
                    {
                        rowStart = computeCoordIndex(startY, itemAreaHeight);
                        var _rowCount2 = computeCoordIndex(renderRect.height, itemAreaHeight);
                        rowEnd = rowStart + _rowCount2;
                        colStart = computeCoordIndex(startX, itemAreaWidth);
                        var _colCount2 = computeCoordIndex(renderRect.width, itemAreaWidth);
                        colEnd = colStart + _colCount2;

                        !direX && colStart > 0 ? colStart-- : colEnd++;
                        !direY && rowStart > 0 ? rowStart-- : rowEnd++;
                    }

            }

            return { rowStart: rowStart, rowEnd: rowEnd, colStart: colStart, colEnd: colEnd };
        }

        /**
         * 根据位置计算坐标index
         * @param location : number    位置
         * @param areaLength : number   单位长度
         * 注意：
         * index从零开始，index为零的item的坐标在坐标原点；
         */

    }, {
        key: 'computeCoordIndex',
        value: function computeCoordIndex(location, areaLength) {
            return Math.floor(location / areaLength);
        }

        /**
         * 根据行列号计算项目的itemIndex
         * @param row : number    行号
         * @param col : number    列号
         * 注意：
         * itemIndex表示的是项目在计算循环的循环单元（循环周期）内的序号；所以如果2个item的 itemIndex 相同，则它们接收的 itemData 也是相同的；
         */

    }, {
        key: 'computeItemIndex',
        value: function computeItemIndex(row, col) {
            var _props4 = this.props,
                roundRowCount = _props4.roundRowCount,
                roundColCount = _props4.roundColCount;


            var rowCount = this.roundRowCount;
            var colCount = this.roundColCount;

            var abs = Math.abs;
            var roundRow = abs(row) % rowCount;
            var roundCol = abs(col) % colCount;

            var itemIndex = 0;
            if (roundRowCount) {
                itemIndex = roundCol * rowCount + roundRow;
            } else {
                itemIndex = roundRow * colCount + roundRow;
            }

            return itemIndex;
        }

        /**
         * 计算ComputeLoop项目的影响样式
         * @param row : number 行号
         * @param col : number 列号
         * @param itemIndex : number    项目序号
         * @returns 样式对象
         */

    }, {
        key: 'computeAffectStyleForComputeLoopItem',
        value: function computeAffectStyleForComputeLoopItem(row, col, itemIndex) {
            var itemRect = this.computeItemRect(row, col);
            return this.computeAffectStyleForItem(null, itemRect, { row: row, col: col, index: itemIndex });
        }
    }, {
        key: 'creationIndexKeyArr',
        value: function creationIndexKeyArr() {
            var indexKeyArr = [];
            var roundItemCount = this.roundItemCount;
            for (var index = 0; index < roundItemCount; index++) {
                indexKeyArr.push([]);
            }
            return indexKeyArr;
        }
    }]);

    return Affecter;
}(PureComponent);

// Affecter的props的类型说明


Affecter.propTypes = {
    affectAnchors: PropTypes.arrayOf(PropTypes.shape({
        x: PropTypes.number,
        y: PropTypes.number
    })),
    itemAnchor: PropTypes.shape({
        x: PropTypes.number,
        y: PropTypes.number
    }),
    transforms: PropTypes.arrayOf(PropTypes.func),
    getItemAffectStyle: PropTypes.func,
    getItemInitStyle: PropTypes.func,
    wrapChildren: PropTypes.bool,
    renderExtendRadius: PropTypes.number,

    //   性能优化
    throttleDelay: PropTypes.number,
    throttleStep: PropTypes.number,

    //   计算循环
    itemSize: PropTypes.shape({
        width: PropTypes.number,
        height: PropTypes.number
    }),
    horSpace: PropTypes.number,
    verSpace: PropTypes.number,
    itemDataArr: PropTypes.array,
    roundRowCount: PropTypes.number,
    roundColCount: PropTypes.number
};

// Affecter的props的默认值
Affecter.defaultProps = {
    renderExtendRadius: 50,
    itemAnchor: { x: 0.5, y: 0.5 },
    throttleDelay: 40,
    throttleStep: 2
};

/**
 * LoopWrap 循环包
 * @props viewRect : {x:number,y:number,width:number,height:number}   容器的视口的滑动偏移矩形
 * @props direcX  : boolean  X轴的滑动方向
 * @props direcY : boolean  Y轴的滑动方向
 * @props loopType ? : "Hor" | "Ver" | "All" | "false"     定义循环类型
 * @props rowCol : "00" | "01" | "10" | "11"      表示wrap位置的标识符
 * @props wrapSpace  ? : number    定义循环包之间的间隔，默认值为0
 */

var LoopWrap = function (_Component) {
    _inherits(LoopWrap, _Component);

    function LoopWrap(props) {
        _classCallCheck(this, LoopWrap);

        var _this2 = _possibleConstructorReturn(this, (LoopWrap.__proto__ || Object.getPrototypeOf(LoopWrap)).call(this, props));

        _this2.setWrapDom = _this2.setWrapDom.bind(_this2);

        _this2.wrapData = { wrapSpace: props.wrapSpace };
        _this2.adjustStyle = {};
        return _this2;
    }

    _createClass(LoopWrap, [{
        key: 'componentDidMount',
        value: function componentDidMount() {
            this.configInitStyle(this.wrapDom);
        }
    }, {
        key: 'componentWillReceiveProps',
        value: function componentWillReceiveProps(nextProps) {
            this.computeAdjustStyle(nextProps);
        }
    }, {
        key: 'computeAdjustStyle',
        value: function computeAdjustStyle(props) {
            var viewRect = props.viewRect,
                direcX = props.direcX,
                direcY = props.direcY,
                wrapSpace = props.wrapSpace,
                loopType = props.loopType;


            var wrapDom = this.wrapDom;

            var x = wrapDom.offsetLeft;
            var y = wrapDom.offsetTop;
            var width = wrapDom.scrollWidth;
            var height = wrapDom.scrollHeight;

            var wrapRect = { x: x, y: y, width: width, height: height };

            var outViewPropX = null;
            var divisorX = null;

            if (direcX) {
                outViewPropX = rectIsInFrontOnHor(wrapRect, viewRect);
                divisorX = 1;
            } else {
                outViewPropX = rectIsInFrontOnHor(viewRect, wrapRect);
                divisorX = -1;
            }

            var outViewPropY = null;
            var divisorY = null;
            if (direcY) {
                outViewPropY = rectIsInFrontOnVer(wrapRect, viewRect);
                divisorY = 1;
            } else {
                outViewPropY = rectIsInFrontOnVer(viewRect, wrapRect);
                divisorY = -1;
            }

            var newX = -1;
            var newY = -1;

            switch (loopType) {
                case "Hor":
                    {

                        if (outViewPropX) {
                            var lengthX = width + wrapSpace;
                            newX = x + 2 * lengthX * divisorX;
                        }
                        break;
                    }

                case "Ver":
                    {
                        if (outViewPropY) {
                            var lengthY = height + wrapSpace;
                            newY = y + 2 * lengthY * divisorY;
                        }
                        break;
                    }

                case "All":
                    {
                        if (outViewPropX) {
                            var _lengthX = width + wrapSpace;
                            newX = x + 2 * _lengthX * divisorX;
                        }

                        if (outViewPropY) {
                            var _lengthY = height + wrapSpace;
                            newY = y + 2 * _lengthY * divisorY;
                        }
                        break;
                    }
            }

            var wrapData = this.wrapData;
            wrapData.height = height;
            wrapData.width = width;
            wrapData.wrapSpace = wrapSpace;

            //Dom 的 scrollLeft 和 scrollTop 滑不到负值，只能为正值；
            if (newX >= 0) {
                wrapData.x = newX;
            }

            if (newY >= 0) {
                wrapData.y = newY;
            }

            this.setWrapData(wrapData);
        }
    }, {
        key: 'setWrapData',
        value: function setWrapData(wrapData) {
            this.wrapData = wrapData;

            var adjustStyle = this.adjustStyle;
            adjustStyle.left = wrapData.x + 'px';
            adjustStyle.top = wrapData.y + 'px';
            adjustStyle.width = wrapData.width + 'px';
            adjustStyle.height = wrapData.height + 'px';
            this.adjustStyle = adjustStyle;
        }
    }, {
        key: 'setWrapDom',
        value: function setWrapDom(element) {
            this.wrapDom = element;
        }
    }, {
        key: 'configInitStyle',
        value: function configInitStyle(wrapElement) {
            var width = wrapElement.scrollWidth;
            var height = wrapElement.scrollHeight;
            var left = 0;
            var top = 0;

            var _props5 = this.props,
                rowCol = _props5.rowCol,
                loopType = _props5.loopType,
                wrapSpace = _props5.wrapSpace;


            switch (loopType) {
                case "Hor":
                    {
                        if (rowCol === "01") {
                            left = width + wrapSpace;
                        }
                        break;
                    }

                case "Ver":
                    {
                        if (rowCol === "01") {
                            top = height + wrapSpace;
                        }
                        break;
                    }

                case "All":
                    {

                        switch (rowCol) {
                            case "01":
                                {
                                    left = width + wrapSpace;
                                    break;
                                }
                            case "10":
                                {
                                    top = height + wrapSpace;
                                    break;
                                }
                            case "11":
                                {
                                    left = width + wrapSpace;
                                    top = height + wrapSpace;
                                    break;
                                }

                        }

                        break;
                    }
            }

            var wrapData = this.wrapData;
            wrapData.x = left;
            wrapData.y = top;
            wrapData.width = width;
            wrapData.height = height;

            this.setWrapData(wrapData);
        }
    }, {
        key: 'render',
        value: function render() {
            var _props6 = this.props,
                viewRect = _props6.viewRect,
                direcX = _props6.direcX,
                direcY = _props6.direcY,
                loopType = _props6.loopType,
                style = _props6.style,
                rowCol = _props6.rowCol,
                wrapSpace = _props6.wrapSpace,
                otherProps = _objectWithoutProperties(_props6, ['viewRect', 'direcX', 'direcY', 'loopType', 'style', 'rowCol', 'wrapSpace']);

            var newStyle = Object.assign({}, style, this.adjustStyle);

            return React.createElement('div', Object.assign({}, otherProps, { style: newStyle, ref: this.setWrapDom }));
        }
    }]);

    return LoopWrap;
}(Component);

/**
 * LoopWrap 的 props 的默认值；
 */


LoopWrap.defaultProps = {
    wrapSpace: 0
};

/*
# AffectedItem ：影响者的项目

## props

以下props是由使用者来定义
renderAll ? : boolean   每次刷新是否渲染所有元素，默认是否，只渲染 渲染矩形 内的元素；

以下props由Affecter组件负责传送，用户不用传送
computeAffectStyleForItem : (itemElement ?: Dom,orItemRect ?:{x = 0,y = 0,width = 0,height = 0})=>StyleObject    计算元素被影响的样式
itemIsInRenderRange   : (itemElement : Dom)=>boolean      用于判断该元素是否在渲染矩形内；
## 使用说明
AffectedItem 组件专门用作 Affecter 组件的子组件，使用格式为：
```
<Affecter>
    <AffectedItem>
        <p>郭斌勇</p>
    </AffectedItem>
</Affecter>
```

您也可创建自己的AffectedItem组件，只需要在需要设置样式时通过 this.props.computeAffectStyleForItem 函数获取新的样式即可；

*/

var AffectedItem = function (_Component2) {
    _inherits(AffectedItem, _Component2);

    function AffectedItem(props) {
        _classCallCheck(this, AffectedItem);

        var _this3 = _possibleConstructorReturn(this, (AffectedItem.__proto__ || Object.getPrototypeOf(AffectedItem)).call(this, props));

        _this3.setThisDom = _this3.setThisDom.bind(_this3);
        return _this3;
    }

    _createClass(AffectedItem, [{
        key: 'componentDidMount',
        value: function componentDidMount() {
            // 兼容低版本React的代码
            this.forceUpdate();
        }
    }, {
        key: 'shouldComponentUpdate',
        value: function shouldComponentUpdate(nextProps, nextState) {
            return nextProps.renderAll || nextProps.itemIsInRenderRange(this.thisDom);
        }
    }, {
        key: 'setThisDom',
        value: function setThisDom(element) {
            this.thisDom = element;
        }
    }, {
        key: 'render',
        value: function render() {
            var _props7 = this.props,
                computeAffectStyleForItem = _props7.computeAffectStyleForItem,
                style = _props7.style,
                itemIsInRenderRange = _props7.itemIsInRenderRange,
                renderAll = _props7.renderAll,
                otherProps = _objectWithoutProperties(_props7, ['computeAffectStyleForItem', 'style', 'itemIsInRenderRange', 'renderAll']);

            var affectStyle = computeAffectStyleForItem(this.thisDom);
            var newStyle = Object.assign({}, style, affectStyle);

            return React.createElement('div', Object.assign({}, otherProps, { style: newStyle, ref: this.setThisDom }));
        }
    }]);

    return AffectedItem;
}(Component);

/*
ComputeLoopItem 计算循环项目的创建函数
@param ItemContentType  计算项目的内容组件


# ComputeLoopItem
@props row  行号
@props col  列号
@props itemIndex    项目序号
@props style    样式
@props itemData    item 的数据





# ItemContentType
@props row  行号
@props col  列号
@props itemIndex    项目序号
@props itemData    item 的数据


 */


function computeLoopItemTypeCreater(ItemContentType) {
    var ComputeLoopItem = function (_Component3) {
        _inherits(ComputeLoopItem, _Component3);

        function ComputeLoopItem() {
            _classCallCheck(this, ComputeLoopItem);

            return _possibleConstructorReturn(this, (ComputeLoopItem.__proto__ || Object.getPrototypeOf(ComputeLoopItem)).apply(this, arguments));
        }

        _createClass(ComputeLoopItem, [{
            key: 'render',
            value: function render() {
                var _props8 = this.props,
                    style = _props8.style,
                    otherProps = _objectWithoutProperties(_props8, ['style']);

                return React.createElement(
                    'div',
                    { style: style },
                    React.createElement(ItemContentType, otherProps)
                );
            }
        }]);

        return ComputeLoopItem;
    }(Component);

    return ComputeLoopItem;
}

export { Affecter, AffectedItem, computeLoopItemTypeCreater };